﻿#pragma once
#include "common.h"

HelperLibBegin

#define THIS_TO_T(ptr) auto ptr = static_cast<T*>(this)

template<class T>
class DragWindowMoveAdapterT
{
public:
  //获取鼠标相对窗口位置坐标
  POINT GetCurCursorRelativePos()
  {
    THIS_TO_T(ptr);
    RECT window_rect{};
    ::GetWindowRect(ptr->GetHWND(), &window_rect);
    POINT cursour_point{};
    ::GetCursorPos(&cursour_point);
    cursour_point.x -= window_rect.left;
    cursour_point.y -= window_rect.top;
    return cursour_point;
  }

  //判断是否是拖拽区域(坐标点相对窗口左上角)
  virtual bool IsDragPos(POINT const&relative_pos) { return false; };

  //设置进入拖拽模式(鼠标必须被记录为按下)
  void SetEnterDragMode(bool capture = true) { ChangeDragMode(true, capture); }
protected:
  //避免子类名称污染
  struct DragData
  {
    POINT cursor_relative_pos_{};
    bool is_drag_{ false };
    bool is_lbutton_down_{ false };
  };

  virtual void OnDrag(POINT pt_move) {}

  void HandleDragMessage(UINT message, WPARAM wParam, LPARAM lParam)
  {
    THIS_TO_T(ptr);
    if (WM_LBUTTONDOWN == message) {
      drag_data_.is_lbutton_down_ = true;
      drag_data_.cursor_relative_pos_.x = LOWORD(lParam);
      drag_data_.cursor_relative_pos_.y = HIWORD(lParam);
      if (BeforeCheckDragPoint(drag_data_.cursor_relative_pos_) && IsDragPos(drag_data_.cursor_relative_pos_)) {
        ChangeDragMode(true);
      }
    } else if (WM_LBUTTONUP == message) {
      drag_data_.is_lbutton_down_ = false;
      ChangeDragMode(false);
    } else if (WM_MOUSEMOVE == message) {
      if (drag_data_.is_drag_) {
        POINT	pt;
        ::GetCursorPos(&pt);
        pt.x -= drag_data_.cursor_relative_pos_.x;
        pt.y -= drag_data_.cursor_relative_pos_.y;
        RECT current_pos{};
        ::GetWindowRect(ptr->GetHWND(), &current_pos);
        ::SetWindowPos(ptr->GetHWND(), NULL, pt.x, pt.y, NULL, NULL, SWP_NOREDRAW | SWP_NOSIZE | SWP_NOSIZE);
        OnDrag({ pt.x - current_pos.left,pt.y - current_pos.top });
      }
    }
  }

  virtual bool BeforeCheckDragPoint(POINT const&relative_pos)
  {
    return true;
  }

  //进入或退出拖拽模式
  void ChangeDragMode(bool is_enter_drag, bool capture = true)
  {
    THIS_TO_T(ptr);
    if (!(is_enter_drag ^ drag_data_.is_drag_)) return;

    if (is_enter_drag && !drag_data_.is_lbutton_down_) return;

    drag_data_.is_drag_ = is_enter_drag;

    if (capture) {
      if (is_enter_drag) {
        ::SetCapture(ptr->GetHWND());
      }
      else {
        if (::GetCapture() == ptr->GetHWND()) {
          ::ReleaseCapture();
        }
      }
    }
  }
protected:
  DragData drag_data_;
};

#undef THIS_TO_T

HelperLibEnd